/**************************************************************************************** 
 Copyright © 2003-2012 hbasesoft Corporation. All rights reserved. Reproduction or       <br>
 transmission in whole or in part, in any form or by any means, electronic, mechanical <br>
 or otherwise, is prohibited without the prior written consent of the copyright owner. <br>
 ****************************************************************************************/
package com.hbasesoft.hamibot.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson2.JSONObject;
import com.hbasesoft.framework.common.GlobalConstants;
import com.hbasesoft.framework.common.utils.date.DateUtil;
import com.hbasesoft.hamibot.Cmd;
import com.hbasesoft.hamibot.dao.AppConfigDao;
import com.hbasesoft.hamibot.dao.AppDao;
import com.hbasesoft.hamibot.dao.TaskDao;
import com.hbasesoft.hamibot.entity.AppConfigEntity;
import com.hbasesoft.hamibot.entity.AppEntity;
import com.hbasesoft.hamibot.entity.TaskEntity;
import com.hbasesoft.hamibot.service.CmdService;
import com.hbasesoft.hamibot.task.AutoTaskHandler;
import com.hbasesoft.hamibot.vo.AppCmdReqVo;
import com.hbasesoft.hamibot.vo.AppVo;
import com.hbasesoft.hamibot.vo.CmdRespVo;
import com.hbasesoft.hamibot.vo.MainCmdReqVo;
import com.hbasesoft.hamibot.vo.TaskRespVo;

import jakarta.annotation.Resource;

/**
 * <Description> <br>
 * 
 * @author ww200<br>
 * @version 1.0<br>
 * @taskId <br>
 * @CreateDate 2024年12月19日 <br>
 * @since V1.0<br>
 * @see com.hbasesoft.hamibot.service <br>
 */
@Service
public class CmdServiceImpl implements CmdService {

    /** app isntance dao */
    @Resource
    private AppDao appDao;

    /** app task */
    @Resource
    private TaskDao taskDao;

    /** 
     * 
     */
    @Resource
    private AppConfigDao appConfigDao;

    /** */
    @Resource
    private AutoTaskHandler taskHandler;

    /**
     * Description: <br>
     * 
     * @author ww200<br>
     * @taskId <br>
     * @param req
     * @return <br>
     */
    @Override
    public CmdRespVo[] chooseApp(final MainCmdReqVo req) {
        List<AppEntity> instAppList = appDao.queryByLambda(
            q -> q.select(AppEntity::getApp).select(AppEntity::getState).eq(AppEntity::getDeviceId, req.getDeviceId())
                .eq(AppEntity::getIsDelete, 0).greaterThanOrEqualTo(AppEntity::getCreateTime, DateUtil.morning()));

        List<String> completeApp = new ArrayList<String>();
        Map<String, AppEntity> processAppMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(instAppList)) {
            for (AppEntity instApp : instAppList) {
                if (instApp.getState() < 0) {
                    completeApp.add(instApp.getApp());
                }
                else {
                    processAppMap.put(instApp.getApp(), instApp);
                }
            }
        }

        // 获取待执行的app列表
        Map<String, List<AppVo>> appList = queryApps().stream()
            .filter(t -> req.getAppNames().contains(t.getGroupName()) && !completeApp.contains(t.getAppName()))
            .collect(Collectors.groupingBy(app -> app.getGroupName()));

        if (MapUtils.isEmpty(appList)) {
            CmdRespVo vo = new CmdRespVo();
            vo.setCode(Cmd.END.getCode());
            return new CmdRespVo[] {
                vo
            };
        }

        AppVo app = sortAppVo(appList.get(new ArrayList<>(appList.keySet()).get(new Random().nextInt(appList.size()))),
            processAppMap).get(0);
        if (!processAppMap.containsKey(app.getAppName())) {
            AppEntity entity = new AppEntity();
            entity.setApp(app.getAppName());
            entity.setDeviceId(req.getDeviceId());
            entity.setCreateBy("");
            entity.setCreateTime(DateUtil.getCurrentDate());
            entity.setIsDelete(0);
            entity.setState((app.getExecTimes() == null ? 1 : app.getExecTimes()) - 1);
            entity.setUpdateBy(entity.getCreateBy());
            entity.setUpdateTime(entity.getCreateTime());
            appDao.save(entity);
        }

        CmdRespVo vo = new CmdRespVo();
        vo.setCode(Cmd.OPEN_APP.getCode());
        vo.setAppName(app.getAppName());
        vo.setParams(Arrays.asList(app.getGroupName()));
        return new CmdRespVo[] {
            vo
        };
    }

    private List<AppVo> sortAppVo(final List<AppVo> vos, final Map<String, AppEntity> processAppMap) {
        Collections.sort(vos, (v1, v2) -> {
            AppEntity v = processAppMap.get(v1.getAppName());
            int v1State = v == null ? 0 : v.getState();
            int v1Exec = v1.getExecTimes() == null ? 1 : v1.getExecTimes();
            v = processAppMap.get(v2.getAppName());
            int v2State = v == null ? 0 : v.getState();
            int v2Exec = v2.getExecTimes() == null ? 1 : v2.getExecTimes();
            return (v1Exec - v1State) - (v2Exec - v2State);
        });
        return vos;
    }

    /**
     * Description: <br>
     * 
     * @author ww200<br>
     * @taskId <br>
     * @param req
     * @return <br>
     */
    @Override
    public CmdRespVo[] taskSubmit(final AppCmdReqVo req) {
        Optional<AppVo> app = queryApps().stream().filter(t -> t.getAppName().equals(req.getAppName())).findFirst();
        if (!app.isPresent()) {
            return null;
        }
        List<TaskEntity> taskList = taskDao.queryByLambda(
            q -> q.eq(TaskEntity::getDeviceId, req.getDeviceId()).eq(TaskEntity::getApp, req.getAppName())
                .eq(TaskEntity::getIsDelete, 0).greaterThanOrEqualTo(TaskEntity::getCreateTime, DateUtil.morning()));

        return excute(app.get(), taskList, req);
    }

    private CmdRespVo[] excute(final AppVo config, final List<TaskEntity> entities, final AppCmdReqVo req) {
        AppEntity appEntity = appDao
            .getByLambda(q -> q.eq(AppEntity::getApp, config.getAppName()).eq(AppEntity::getDeviceId, req.getDeviceId())
                .eq(AppEntity::getIsDelete, 0).greaterThanOrEqualTo(AppEntity::getCreateTime, DateUtil.morning()));

        TaskRespVo taskVo = taskHandler.execute(config, appEntity, entities, req);
        if (taskVo != null) {
            String newFlowState = ArrayUtils.isNotEmpty(taskVo.getCmds()) ? taskVo.getCmds()[0].getTaskId() : null;
            if (taskVo.getAppEntity() != null) {
                AppEntity newAppEntity = taskVo.getAppEntity();
                if (newAppEntity.getId() == null) {
                    appDao.save(newAppEntity);
                }
                else if (taskVo.isAppComplete()) {
                    int state = appEntity == null ? 0 : appEntity.getState();
                    appDao.updateByLambda(q -> q.set(AppEntity::getState, state - 1)
                        .set(AppEntity::getUpdateTime, DateUtil.getCurrentDate())
                        .eq(AppEntity::getId, appEntity.getId()));
                    taskDao.updateByLambda(
                        q -> q.set(TaskEntity::getIsDelete, 1).set(TaskEntity::getDeleteBy, "应用完成" + state)
                            .set(TaskEntity::getDeleteTime, DateUtil.getCurrentDate())
                            .eq(TaskEntity::getApp, config.getAppName()).eq(TaskEntity::getDeviceId, req.getDeviceId())
                            .eq(TaskEntity::getIsDelete, 0));
                }
                else if (StringUtils.isNotEmpty(newFlowState) && !StringUtils.equals(newFlowState, req.getTaskId())) {
                    appDao.updateByLambda(q -> q.set(AppEntity::getUpdateTime, DateUtil.getCurrentDate())
                        .eq(AppEntity::getId, appEntity.getId()));
                }
            }

            if (CollectionUtils.isNotEmpty(taskVo.getEntities())) {

                for (TaskEntity t : taskVo.getEntities()) {
                    if (StringUtils.isNotEmpty(t.getFlowState())) {
                        if (t.getId() == null) {
                            t.setCreateBy(GlobalConstants.BLANK);
                            t.setCreateTime(DateUtil.getCurrentDate());
                            t.setUpdateBy(t.getCreateBy());
                            t.setUpdateTime(t.getCreateTime());
                            t.setIsDelete(0);
                            taskDao.save(t);
                        }
                        else {
                            taskDao.updateByLambda(q -> q.set(TaskEntity::getFlowState, t.getFlowState())
                                .set(TaskEntity::getContent, t.getContent())
                                .set(TaskEntity::getUpdateTime, DateUtil.getCurrentDate())
                                .eq(TaskEntity::getId, t.getId())
                                .greaterThanOrEqualTo(TaskEntity::getCreateTime, DateUtil.morning()));
                        }
                    }

                }
            }
            return taskVo.getCmds();
        }
        return null;
    }

    private List<AppVo> queryApps() {
        List<AppConfigEntity> list = appConfigDao
            .queryByLambda(q -> q.eq(AppConfigEntity::getIsDelete, 0).orderByDesc(AppConfigEntity::getUpdateTime));
        if (CollectionUtils.isNotEmpty(list)) {
            return list.stream().map(app -> {
                AppVo vo = JSONObject.parseObject(app.getContent(), AppVo.class);
                vo.setId(app.getId());
                return vo;
            }).sorted((t1, t2) -> t1.getSeq() - t2.getSeq()).toList();
        }
        return new ArrayList<>();

    }

}
